﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
using System.Threading;
using System.Configuration;
using ProtocoloCarSharing;
using System.Collections;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting.Channels.Tcp;

namespace ServidorGestionClientes
{
    public class Sistema
    {
        private static Sistema Instancia = null;
        private List<Cliente> listaClientes;
        static MessageQueue queueReceptor;
        static MessageQueue queueEnvioCobro;
        static MessageQueue queueEnvioComunicaciones;

        public static Sistema GetInstancia()
        {
            if (Instancia == null)
            {
                Instancia = new Sistema();
            }

            return Instancia;
        }

        private Sistema()
        {
            try
            {
                IniciarRemoting();

                string nomMQServGestion = (ConfigurationManager.AppSettings["nomMSMQServGestion"]);

                string nomMQServCobro = (ConfigurationManager.AppSettings["nomMSMQServCobro"]);

                string nomMQServComunicaciones = (ConfigurationManager.AppSettings["nomMSMQServComunicacion"]);

                if (MessageQueue.Exists(nomMQServGestion))
                {
                    queueReceptor = new MessageQueue(nomMQServGestion);
                }
                else
                {
                    queueReceptor = MessageQueue.Create(nomMQServGestion);
                }
                queueReceptor.Formatter = new XmlMessageFormatter(new Type[] { typeof(MensajeProtocolo) });

                queueEnvioCobro = new MessageQueue(nomMQServCobro);

                queueEnvioComunicaciones = new MessageQueue(nomMQServComunicaciones);

                ThreadPool.QueueUserWorkItem(new WaitCallback(EscucharMSMQ));
                listaClientes = new List<Cliente>();
            }
            catch (Exception)
            {
                throw new Exception();
            }

        }

        public List<Cliente> ListaClientes
        {
            get { return listaClientes; }
            set { listaClientes = value; }
        }

        public void AgregarCliente(string nombre, string contrasenia, bool esPos)
        {
            Cliente cliente = new Cliente(nombre, contrasenia, esPos);
            listaClientes.Add(cliente);
        }

        public void ModificarCliente(string nombreViejo, string nomNuevo, string cont, bool esPos)
        {
            foreach (Cliente cli in listaClientes)
            {
                if (cli.Nombre.Equals(nombreViejo))
                {
                    cli.Nombre = nomNuevo;
                    cli.Contrasenia = cont;
                    if (esPos != cli.EsPosPago)
                    {
                        if (cli.EsPosPago)
                        {
                            cli.Saldo = -(cli.Saldo) + cli.SaldoExtra;
                        }
                        else
                        {
                            if (cli.Saldo > 0)
                            {
                                cli.SaldoExtra = cli.Saldo;
                                cli.Saldo = 0;
                            }
                            else
                            {
                                cli.Saldo = -cli.Saldo;
                            }
                        }
                    }
                    cli.EsPosPago = esPos;
                }
            }
        }

        public void EliminarCliente(string nombre)
        {
            Cliente cli = new Cliente(nombre, " ", false);
            listaClientes.Remove(cli);
        }

        public bool ExisteCliente(string nomCliente)
        {
            return listaClientes.Contains(new Cliente(nomCliente, "", false));
        }

        public void AgregarSaldo(Cliente cli, long montoAsumar)
        {
            foreach (Cliente c in listaClientes)
            {
                if (c.Equals(cli))
                {
                    c.RecargarSaldo(montoAsumar);
                }
            }
        }

        public List<Cliente> ListaClientesPrePagos()
        {
            List<Cliente> lisRet = new List<Cliente>();
            foreach (Cliente c in listaClientes)
            {
                if (!c.EsPosPago)
                {
                    lisRet.Add(c);
                }
            }
            return lisRet;
        }

        public bool LogCliente(string nom, string con)
        {
            foreach (Cliente c in listaClientes)
            {
                if (c.Nombre.Equals(nom))
                {
                    return c.Contrasenia.Equals(con);
                }
            }
            return false;
        }

        private void EscucharMSMQ(Object o)
        {
            while (true)
            {
                Message msg = queueReceptor.Receive();
                string labelMsg = msg.Label;
                switch (labelMsg)
                {
                    case "ValidarCliente":
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ValidarClienteMSMQ), msg);
                        break;

                    case "Consumo":
                        ThreadPool.QueueUserWorkItem(new WaitCallback(ConsumoClienteMSMQ), msg);
                        break;

                    case "EstadoViaje":
                        ThreadPool.QueueUserWorkItem(new WaitCallback(EstadoMSMQ), msg);
                        break;

                    default:
                        break;
                }
            }
        }

        private void ValidarClienteMSMQ(Object o)
        {
            Message msg = (Message)o;
            MensajeProtocolo msjRecibido = (MensajeProtocolo)msg.Body;
            string idMsg = msg.Id;
            List<string> datos = msjRecibido.InfoDatos();
            string datosAEnviar = datos[0] + Protocolo.SEPARADOR + Protocolo.ERROR;
            if (LogCliente(datos[0], datos[1]))
            {
                datosAEnviar = datos[0] + Protocolo.SEPARADOR + Protocolo.OK;
            }
            MensajeProtocolo msjRes = new MensajeProtocolo(Protocolo.Header.RES, Protocolo.Comando.IDENTIFICAR_USUARIO, datosAEnviar.Length, datosAEnviar);
            EnviarMSMQComunicaciones("Servidor gestion clietnes", msjRes, idMsg);
        }

        private void ConsumoClienteMSMQ(Object o)
        {
            Message msg = (Message)o;
            MensajeProtocolo msjRecibido = (MensajeProtocolo)msg.Body;
            string idMsg = msg.Id;
            List<string> datos = msjRecibido.InfoDatos();
            string datosAEnviar = datos[0] + Protocolo.SEPARADOR + Protocolo.ERROR;
            if (LogCliente(datos[0], datos[1]))
            {
                foreach (Cliente c in listaClientes)
                {
                    if (c.Nombre.Equals(datos[0]))
                    {
                        bool error = false;
                        int x = 0;
                        int y = 0;
                        DateTime fecha;
                        int distancia = 0;
                        long consumoPlata = 0;
                        try
                        {
                            x = int.Parse(datos[2]);
                            y = int.Parse(datos[3]);
                            fecha = DateTime.Parse(datos[6]);
                            distancia = int.Parse(datos[4]);
                            consumoPlata = long.Parse(datos[4]);
                        }
                        catch (FormatException)
                        {
                            error = true;
                            fecha = DateTime.Now;
                        }
                        if (!error) c.ActualizarPosicion(x, y);
                        c.AgregarConsumo(distancia, consumoPlata, fecha);
                        c.DescontarSaldo(consumoPlata);
                        if (!c.EsPosPago && c.Saldo < 20)
                        {
                            datosAEnviar = datos[0] + Protocolo.SEPARADOR + Protocolo.SIN_SALDO_DISP;
                        }
                        else
                        {
                            datosAEnviar = datos[0] + Protocolo.SEPARADOR + Protocolo.OK;
                        }
                        if (datos[7].Equals(Protocolo.EN_SERVICIO))
                        {
                            c.EstaEnFuncionamiento = true;
                        }
                        else
                        {
                            c.EstaEnFuncionamiento = false;
                        }
                    }
                }
            }
            MensajeProtocolo msjRes = new MensajeProtocolo(Protocolo.Header.RES, Protocolo.Comando.CONSUMO, datosAEnviar.Length, datosAEnviar);
            EnviarMSMQCobro("Servidor gestion clietnes", msjRes, idMsg);
        }

        private void EstadoMSMQ(Object o)
        {
            Message msg = (Message)o;
            MensajeProtocolo msjRecibido = (MensajeProtocolo)msg.Body;
            string idMsg = msg.Id;
            List<string> datos = msjRecibido.InfoDatos();
            string datoAImprimir = "ID Auto = " + datos[0] + "  Posición : [ " + datos[1] + ", " + datos[2] + " ]    KmtotalVehiculo = " + datos[3];
            //
        }

        private void EnviarMSMQCobro(string label, MensajeProtocolo msj, string idMsg)
        {
            if (queueEnvioCobro == null)
            {
                string nomMQServCobro = (ConfigurationManager.AppSettings["nomMSMQServCobro"]);
                queueEnvioCobro = new MessageQueue(nomMQServCobro);
            }
            Message msgAEnviar = new Message(msj);
            msgAEnviar.CorrelationId = idMsg;
            queueEnvioCobro.Send(msgAEnviar, label);
        }

        private void EnviarMSMQComunicaciones(string label, MensajeProtocolo msj, string idMsg)
        {
            if (queueEnvioComunicaciones == null)
            {
                string nomMSMQServComunicacion = (ConfigurationManager.AppSettings["nomMSMQServComunicacion"]);
                queueEnvioComunicaciones = new MessageQueue(nomMSMQServComunicacion);
            }
            Message msgAEnviar = new Message(msj);
            msgAEnviar.CorrelationId = idMsg;
            queueEnvioComunicaciones.Send(msgAEnviar, label);
        }

        private void IniciarRemoting() 
        {
            try
            {
                IDictionary props = new Hashtable();
                string puerto = (ConfigurationManager.AppSettings["puertoRemotigWs"]);
                string nomRemoting = (ConfigurationManager.AppSettings["nombreServRemotingWS"]);
                props["port"] = puerto;
                props["name"] = "CanalRemotingWs";
                BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
                serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
                IChannel chan = new TcpChannel(props, null, serverProvider);
                ChannelServices.RegisterChannel(chan, false);
                RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemotingWS), nomRemoting, WellKnownObjectMode.SingleCall);
            }
            catch (Exception)
            {
                throw new Exception();
            }
        }
    }

}
